Imports System.IO
Imports System.Math
Imports System.Text
Imports System.Drawing
Imports System.Drawing.Graphics
Imports System.Drawing.Drawing2D
Imports System.Runtime.Serialization.Formatters.Binary
Imports System.Windows.Forms


<Serializable()> _
Public Class GraphPath
    Public Points As ArrayList
    Public pathClosed As Boolean = False

    Public Sub ClosePath()
        ' add a final point which is the same as the first point
        Dim pnt As New Point()
        pnt = Points(0)
        Points.Add(pnt)
    End Sub

End Class


Public Structure FMPScale
    Public PixelCount As Integer
    Public MilesPerPixelCount As Integer
End Structure


<Serializable()> Public Class Feature
    Public featureID As Int16 = 0
    Public featureName As String = "not named"
    Public LOS As Int16 = 0
    Public moveValue As Int16 = 0
    Public defensive As Int16 = 0
    Public description As String = "Enter a description."
    Public sdescription As String = "Short description"
    Public texture As Bitmap
    Public moveClass As Int16 = 0
    <NonSerialized()> Public graphPaths As New ArrayList(1)
    Friend specialPaths As New ArrayList(25)        ' collection of points to create a path
    Public isCoveringTerrain As Boolean = False
    Public currentPathComplete As Boolean = True
    Private ExtendedProperties As String         ' this is xml data while saved
    <NonSerialized()> Public ExtendedPropertiesXML As Xml.XmlDocument


    Public Sub New()
        ' add an off-map graphic path, so each feature always has something to draw.
        Dim gp As New GraphicsPath

        gp.AddLine(0, 0, 0, 0)
        gp.AddLine(0, 0, 0, 0)
        gp.CloseFigure()

        graphPaths.Add(gp)

    End Sub

    Public Sub fillSpecialPaths()
        ' called on save
        Try
            Dim gp As GraphicsPath

            For Each gp In graphPaths
                specialPaths.Add(gp.PathPoints)
            Next
        Catch ex As Exception
            HandleException("Feature.fillSpecialPaths", ex)
        End Try

    End Sub

    Public Sub fillGraphPaths()
        ' sends the special path data to graphpaths, adds to it, so if there is data there already it could be duplicated.
        ' called on load
        Try
            Dim pnts As Object

            If graphPaths Is Nothing Then graphPaths = New ArrayList(25)

            For Each pnts In specialPaths
                ' add a new graphpath
                Dim gp As New GraphicsPath

                gp.AddLines(pnts)
                gp.CloseFigure()

                graphPaths.Add(gp)

            Next

            ' clear the specialpath data... maybe
            specialPaths.Clear()

        Catch ex As Exception
            HandleException("Feature.fillGraphPaths", ex)
        End Try
    End Sub

    Public Sub New(ByVal bmp As Bitmap)
        texture = bmp
    End Sub

    Public Overrides Function ToString() As String
        Return Me.featureName
    End Function

    Public Function Clone() As Feature
        Dim m As New MemoryStream
        Dim b As New BinaryFormatter
        'Dim f As New Feature(Me.texture)
        Dim f As New ElevationFeature

        b.Serialize(m, f)
        m.Position = 0

        Return b.Deserialize(m)


    End Function

End Class


<Serializable()> Public Class Elevation
    Inherits Feature

    Private _elevationrank As Integer '// basically, what level it is. 

    Public Property ElevationRank() As Integer
        Get
            Return _elevationrank
        End Get
        Set(ByVal value As Integer)
            _elevationrank = value
        End Set
    End Property

End Class


<Serializable()> Public Class MinorRoadFeature
    Inherits Feature

    Public Sub New()
        MyBase.New()

        MyBase.sdescription = "Road"
        MyBase.description = "Small roadways that typically link larger roadways."

        ' build the texture
        BuildTexture()
    End Sub

    Private Sub BuildTexture()
        texture = New Bitmap(4, 4)
        Dim baseGraphics As Graphics = Graphics.FromImage(texture)

        baseGraphics.FillRectangle(Brushes.Brown, New RectangleF(0, 0, 4, 4))
    End Sub

End Class


<Serializable()> Public Class MajorRoadFeature
    Inherits Feature

    Public Sub New()
        MyBase.New()
        MyBase.sdescription = "Major Road"
        MyBase.description = "Major roadways that allow massive amounts of traffic."
        BuildTexture()
    End Sub

    Private Sub BuildTexture()
        texture = New Bitmap(4, 4)
        Dim baseGraphics As Graphics = Graphics.FromImage(texture)

        baseGraphics.FillRectangle(Brushes.Brown, New RectangleF(0, 0, 4, 4))
    End Sub
End Class


<Serializable()> Public Class Road
    Private m_code As Char ' the code, either u, i or m 'unimproved, improved or major
    Private m_x1 As Single
    Private m_y1 As Single
    Private m_x2 As Single
    Private m_y2 As Single
    Private m_RoadType As FlexMapData.RoadType

    Public ReadOnly Property Code() As Char
        Get
            Return m_code
        End Get
    End Property

    Public Property RoadType() As FlexMapData.RoadType
        Get
            Return m_RoadType
        End Get
        Set(ByVal Value As FlexMapData.RoadType)
            m_RoadType = Value
            Select Case m_RoadType
                Case FlexMapData.RoadType.Unimproved : m_code = "u"
                Case FlexMapData.RoadType.ImprovedRoad : m_code = "i"
                Case FlexMapData.RoadType.MajorRoad : m_code = "m"
            End Select
        End Set
    End Property

    Public Property StartPoint() As Point
        Get
            Dim p As New Point(X1, Y1)
            Return p
        End Get
        Set(ByVal Value As Point)
            X1 = Value.X
            Y1 = Value.Y
        End Set
    End Property

    Public Property EndPoint() As Point
        Get
            Dim p As New Point(X2, Y2)
            Return p
        End Get
        Set(ByVal Value As Point)
            X2 = Value.X
            Y2 = Value.Y
        End Set
    End Property

    Public Property X1() As Single
        Get
            Return m_x1
        End Get
        Set(ByVal Value As Single)
            m_x1 = Value
        End Set
    End Property

    Public Property Y1() As Single
        Get
            Return m_y1
        End Get
        Set(ByVal Value As Single)
            m_y1 = Value
        End Set
    End Property

    Public Property X2() As Single
        Get
            Return m_x2
        End Get
        Set(ByVal Value As Single)
            m_x2 = Value
        End Set
    End Property

    Public Property Y2() As Single
        Get
            Return m_y2
        End Get
        Set(ByVal Value As Single)
            m_y2 = Value
        End Set
    End Property

End Class


Public Class Roads
    Inherits CollectionBase

    Public Sub New()
        MyBase.New()
    End Sub

    Default Public Property Item(ByVal index As Integer) As Road
        Get
            Return innerlist.Item(index)
        End Get
        Set(ByVal Value As Road)
            innerlist.Item(index) = Value
        End Set
    End Property

    Public Sub Add(ByRef item As Road)
        innerlist.Add(item)
    End Sub

    Public Sub Remove(ByRef item As Road)
        innerlist.Remove(item)
    End Sub

End Class


Public Class SMVector
    Private m_dx As Single
    Private m_dy As Single
    Private m_StPt As PointF

    Public Property StartPoint() As PointF
        Get
            Return m_StPt
        End Get
        Set(ByVal Value As PointF)
            m_StPt = Value
        End Set
    End Property

    Public Function Distance(ByVal dx As Double, ByVal dy As Double) As Double

        Distance = (dx ^ 2 + dy ^ 2) ^ 0.5

    End Function

    Public Function VectorAngle(ByVal dx As Double, ByVal dy As Double) As Double
        Dim dblVecDist As Double

        dblVecDist = Me.Distance(dx, dy)

        If dx > 0 And dy > 0 Then 'q1
            Return Math.Atan2(dy, dx)
            'Return Math.Cos(dx / dblVecDist)
        ElseIf dx < 0 And dy > 0 Then 'q2
            Return Math.Atan2(dy, dx)
        ElseIf dx < 0 And dy < 0 Then 'q3
            Return Math.Atan2(dy, dx)
        ElseIf dx > 0 And dy < 0 Then 'q4
            Return Math.Atan2(dy, dx)
        ElseIf dx = 0 And dy < 0 Then
            Return 3 * PI / 2
        ElseIf dx = 0 And dy > 0 Then
            Return PI / 2
        ElseIf dy = 0 And dx > 0 Then
            Return 0.0
        ElseIf dy = 0 And dx < 0 Then
            Return PI
        End If

    End Function

    Public Function GeneratePtOnVector(ByVal StPt As PointF, ByVal EndPt As PointF, ByVal distFromStPt As Single) As PointF

        'Given D = distance
        'ax + by + D = 0
        'cos (theta) = x/D
        'X = D * cos(theta)
        'Y = D * sin (theta)
        'sin (theta) = y/D
        'pythagorean 
        '(x)^2 + (y) ^2 = (D) ^2
        'x = (D^2 - y^2)^.5
        'y = (D^2 - x^2)^.5
        Dim dx As Double = EndPt.X - StPt.X
        Dim dy As Double = EndPt.Y - StPt.Y

        Dim theta As Double = VectorAngle(dx, dy)

        Dim x As Double = distFromStPt * Math.Cos(theta)
        Dim y As Double = distFromStPt * Math.Sin(theta)

        GeneratePtOnVector = New PointF(CType(StPt.X + x, Single), CType(StPt.Y + y, Single))

    End Function

    Public Property DX() As Single
        Get
            Return m_dx
        End Get
        Set(ByVal Value As Single)
            m_dx = Value
        End Set
    End Property

    Public Property DY() As Single
        Get
            Return m_dy
        End Get
        Set(ByVal Value As Single)
            m_dy = Value
        End Set
    End Property

    Public Sub New(ByVal dx As Single, ByVal dy As Single)
        m_dx = dx
        m_dy = dy
    End Sub

    Public Sub New(ByVal dx As Single, ByVal dy As Single, ByVal stPt As PointF)
        m_dx = dx
        m_dy = dy
        Me.StartPoint = stPt
    End Sub

    Public Sub New()

    End Sub

    Public Function Subtract(ByVal vec1 As SMVector, ByVal vec2 As SMVector) As SMVector
        Dim vecOut As SMVector
        vecOut = New SMVector
        vecOut.DX = vec2.DX - vec1.DX
        vecOut.DY = vec2.DY - vec1.DY
        Return vecOut
    End Function

    Public Function Add(ByVal vec1 As SMVector, ByVal vec2 As SMVector) As SMVector

        Dim vecOut As SMVector
        vecOut = New SMVector
        vecOut.DX = vec1.DX + vec2.DX
        vecOut.DY = vec1.DY + vec2.DY
        Return vecOut

    End Function
End Class


<Serializable()> Public Class FlexMapData
    ' this is the basic class used to display and edit a flexmap
    Const IMPASSABLEVALUE As Integer = 11
    Const CHUNKSIZE As Integer = 10
    Const MAXMOVEMENTTYPES As Integer = 10   ' the highest number acceptable for movement class information.
    Const MAXMAPSIZE As Integer = 1600
    Const MINMAPSIZE As Integer = 100

    Public Event RenderRequest()
    Public Event RenderComplete()

    Public Enum BridgeType
        Destroyed = 0
        [_firstenum] = 0    ' this is put here so that 'AValue' is used instead of '[_firstenum]'
        temporaryBridge = 1
        smallBridge = 2
        mediumBridge = 3
        heavyBridge = 4
        railBridge = 5
        noBridge = 255
        [_lastenum]
    End Enum

    Public Enum RoadType
        Unimproved = 0
        [_firstenum] = 0
        ImprovedRoad = 1
        MajorRoad = 2
    End Enum

    <Serializable()> Public Class MapLabel
        Public labelID As Int32
        Public text As String
        Public x As Int32
        Public y As Int32
        Public foreColor As Color
        Public boundingBox As Boolean
        Public backgroundColor As Color

        Overrides Function ToString() As String
            Return text
        End Function

        Public Function AsString() As String
            ' returns a string with space delimited properties. text is represented as: 'some_text'
            Dim sb As New System.Text.StringBuilder
            Dim sbReturnString As New System.Text.StringBuilder
            sbReturnString.Append("LabelType1 ")
            sb.Append(text)
            sb.Replace(" ", "_")

            sbReturnString.Append(sb.ToString & " ")
            sbReturnString.Append(x & " ")
            sbReturnString.Append(y & " ")
            sbReturnString.Append(foreColor.R & " " & foreColor.G & " " & foreColor.B & " ")
            sbReturnString.Append(boundingBox & " ")
            sbReturnString.Append(backgroundColor.R & " " & backgroundColor.G & " " & backgroundColor.B)

            Return sbReturnString.ToString
        End Function

        Public Sub FromString(ByVal value As String)
            ' populates the properties for this label from the value provided
            Try
                Dim wp As New WordParser(value)
                Dim arr As New ArrayList(11)
                Dim s As String

                For Each s In wp
                    arr.Add(s)
                Next

                ' set the properties
                text = arr(1) ' this still has the underscore character
                text = text.Replace("_", " ") ' not no more!
                x = arr(2)
                y = arr(3)
                foreColor = Color.FromArgb(arr(4), arr(5), arr(6))
                If arr(7) = "true" Then
                    boundingBox = True
                Else
                    boundingBox = False
                End If
                backgroundColor = Color.FromArgb(arr(8), arr(9), arr(10))

            Catch ex As Exception
                ' this is an invalid label. :( sadness
            End Try
        End Sub

    End Class

    Public MapName As String = "defmap"
    Public Width As Int16 = 600
    Public Height As Int16 = 600
    Public MapVersion As String = "1.0.0c"
    Public Features As ArrayList
    <NonSerialized()> Private bFeatureregionsInitialized As Boolean = False
    <NonSerialized()> Private FeatureRegs As New FeaturesWithRegions
    Public MoveTypeLookup(10) As String    ' "infantry, mechanized", etc correspond the the 'y' axis in movementvalues
    Public MovementValues(9, 10) As Int16  ' (x,y) where x is movement class index, y is move type index and result (x,y) is modifier
    Public BridgeMoveAllowances(9, 10) As Int16  ' (x,y) where x is bridge type temp, small etc, and y is move type  and result (x,y) is if the movetype can use it 0 no, 1 yes
    Public MoveClassLookup(9) As String
    Public Labels As New ArrayList(15)          ' an arraylist of maplabels
    Public Bridges As New ArrayList(10)         ' an arraylist of bridges
    Public MapDescription As String
    Private ExtendedProperties As String = "50 1"       ' this is xml data while saved (right now, its only map scale: 'x y' where x is # pixels, y is # miles.
    Private _unitName As String = "Mi." '// this can be MILE, KM, whatever... 

    <NonSerialized()> Public ExtendedPropertiesXML As Xml.XmlDocument
    '<NonSerialized()> Private ExtendedPropertyNames As String = "Pixels Miles"
    <NonSerialized()> Public TemporaryLabels As New MapLabels_fmp   ' handles the labels that are stored in a cgd.
    <NonSerialized()> Public Roads As New Roads ' roads must be added to the extended properties when saving.

    Protected colorsList As New ArrayList(20)
    Private baseFeatureDefined As Boolean = False
    <NonSerialized()> Private Editable As Boolean = True
    <NonSerialized()> Private BaseBMP As Bitmap

    '// these are used for the movement allowed matrix. If movement is possible, the value is added.
    Const C_MoveType0 As Integer = 1
    Const C_MoveType1 As Integer = 2
    Const C_MoveType2 As Integer = 4
    Const C_MoveType3 As Integer = 8
    Const C_MoveType4 As Integer = 16
    Const C_MoveType5 As Integer = 32
    Const C_MoveType6 As Integer = 64
    Const C_MoveType7 As Integer = 128
    Const C_MoveType8 As Integer = 256
    Const C_MoveType9 As Integer = 512
    Const C_MoveType10 As Integer = 1024

    Public Sub New()
        Features = New ArrayList(16)
        DefineDefaults()

    End Sub

    Public Property unitName() As String
        Get
            Return _unitname
        End Get
        Set(ByVal value As String)
            _unitname = value
        End Set
    End Property

    Public Sub ResetDefaults()
        DefineDefaults()
    End Sub

    Public Function PixelsPerOneMile() As Integer
        Return MapScale.PixelCount / MapScale.MilesPerPixelCount
    End Function

    Public Sub TerrainInfoAtCoord(ByVal location As Point, ByRef TerrainName As String, ByRef TerrainDescription As String)
        Try
            Dim feat As Feature
            Dim i As Int32
            Dim PathIndex As Int32
            Dim lastCovering As Boolean = False
            Dim featureName As String = ""
            Dim coverDesc As String = ""

            For i = Features.Count - 1 To 0 Step -1
                ' go backwards dude.
                feat = Features.Item(i)
                For PathIndex = 0 To feat.graphPaths.Count
                    If feat.graphPaths.Count > 0 Then
                        Dim reg As New Region
                        Try
                            ' add each graphics path to this feature's region so we can see if the coords bounce it.
                            reg.Intersect(feat.graphPaths(PathIndex))
                            If reg.IsVisible(location.X, location.Y) Then
                                If feat.isCoveringTerrain Then
                                    ' we should continue looking at features... 
                                    If lastCovering = True Then
                                        ' we don't do anything with multiple covering terrains... 
                                    Else
                                        featureName = feat.featureName
                                        If Not feat.description.Length = 0 Then coverDesc = "(" & feat.description & ")"
                                        lastCovering = True
                                    End If
                                Else
                                    ' we found the topmost non-covering terrain, so we are done.
                                    featureName = featureName & ": " & feat.featureName
                                    TerrainName = featureName
                                    TerrainDescription = feat.description
                                    If Not coverDesc.Length = 0 Then TerrainDescription &= " " & coverDesc
                                    Exit Sub
                                End If
                            End If
                        Catch ex As Exception
                            'HandleException("FlexMapData.TerrainInfoAtCoord", ex)
                        End Try
                    End If
                Next PathIndex
            Next i

        Catch ex As Exception

        End Try
    End Sub

    Public Function TerrainNameAtCoord(ByVal location As Point) As String
        ' returns a name of the type of terrain at the coordinate
        Try
            Dim feat As Feature
            Dim i As Int32
            Dim PathIndex As Int32
            Dim lastCovering As Boolean = False
            Dim featureName As String = ""

            For i = Features.Count - 1 To 0 Step -1
                ' go backwards dude.
                feat = Features.Item(i)
                For PathIndex = 0 To feat.graphPaths.Count
                    Dim reg As New Region
                    Try
                        ' add each graphics path to this feature's region so we can see if the coords bounce it.
                        reg.Intersect(feat.graphPaths(PathIndex))
                        If reg.IsVisible(location.X, location.Y) Then
                            If feat.isCoveringTerrain Then
                                ' we should continue looking at features... 
                                If lastCovering = True Then
                                    ' we don't do anything with multiple covering terrains... 
                                Else
                                    featureName = feat.featureName
                                    lastCovering = True
                                End If
                            Else
                                ' we found the topmost non-covering terrain, so we are done.
                                featureName = featureName & ": " & feat.featureName
                                Return featureName
                                Exit Function
                            End If
                        End If
                    Catch
                        ' we don't have to do anything. Really.
                    End Try
                Next PathIndex
            Next i
            ' if all else fails:
            Return "Undefined Terrain"
        Catch ex As Exception
            HandleException("FlexMapData.TerrainNameAtCoord", ex)
            Return "Undefined Terrain"
        End Try

    End Function

    Public Function GenerateMovementAllowedMatrixBadazz(Optional ByVal sampleSize As Integer = 3) As StringBuilder

        Dim a As Integer
        Dim b As Integer
        Dim i As Integer

        Dim sbmatrix As New StringBuilder
        Dim testpoint As Point
        Dim totaltps As Integer = (Width / sampleSize) * (Height / sampleSize)
        Dim currenttps As Integer
        Dim value As Integer
        Dim feats As New FeaturesWithRegions
        Dim feat As Feature
        Dim pathindex As Integer

        Dim tickstart As Long = Now.Ticks

        '// build up our feature regions (from top to bottom)
        For i = Features.Count - 1 To 0 Step -1
            ' go backwards dude.
            feat = Features.Item(i)

            Dim featregs As New FeatureRegions
            featregs.ParentFeature = feat

            '// now create a region for each path in there and add it to the collection
            For pathindex = 0 To feat.graphPaths.Count - 1
                ' was dim reg as new region
                Dim reg As New Region

                ' add each graphics path to this feature's region so we can see if the coords bounce it.
                reg.Intersect(feat.graphPaths(pathindex))
                featregs.Add(reg)

            Next pathindex

            '// add this featureregions to the collection
            feats.Add(featregs)

        Next i

        For a = 1 To Width / sampleSize
            For b = 1 To Height / sampleSize

                currenttps += 1

                testpoint.X = a * sampleSize
                testpoint.Y = b * sampleSize
                value = 0
                value = MovementAllowed(testpoint, 0, feats)
                sbmatrix = sbmatrix.Append(value.ToString & " ")

#If DEBUG Then
                '  Debug.WriteLine(currenttps / totaltps & "% finished.")
#End If

            Next
        Next

        Dim tickend As Long = Now.Ticks

        Return sbmatrix

    End Function


    Public Function GenerateMovementAllowedMatrix(Optional ByVal sampleSize As Integer = 3) As StringBuilder 'Integer(,)
        '// move across the map and fill in a matrix to populate a series of points for movement allowed... crap...
        Dim a As Integer
        Dim b As Integer

        Dim SBMatrix As New StringBuilder
        Dim testPoint As Point
        Dim moveclassID As Integer
        Dim totalTPs As Integer = (Width / sampleSize) * (Height / sampleSize)
        Dim currentTPs As Integer
        Dim value As Integer

        Dim tickstart As Long
        Dim tickend As Long

        tickstart = Now.Ticks

        Try
            For a = 1 To Width / sampleSize
                For b = 1 To Height / sampleSize

                    currentTPs += 1

                    testPoint.X = a * sampleSize
                    testPoint.Y = b * sampleSize

                    'value = AllMovementsAllowed(testPoint)
                    value = 0
                    For moveclassID = 0 To 10
                        'If MovementAllowed(testPoint, moveclassID) Then
                        '    If moveclassID = 0 Then
                        '        value = 1
                        '    Else
                        '        value += Pow(2, moveclassID)
                        '    End If
                        'End If
                    Next
                    SBMatrix = SBMatrix.Append(value.ToString & " ")

#If DEBUG Then
                    Debug.WriteLine(currentTPs / totalTPs & "% finished.")
#End If
                Next
            Next
            'Return MAMatrix

            tickend = Now.Ticks

            MessageBox.Show(Round((tickend - tickstart) / 10000000, 2) & " seconds to complete.")

            Return SBMatrix

        Catch ex As Exception
            HandleException("FlexMapData.GenerateMovementAllowedMatrix", ex)
            Return SBMatrix
        End Try

    End Function

    Public Function MovementFromAToBAllowed(ByVal start As Point, ByVal endP As Point, ByVal moveType As Integer) As Boolean
        Try
            Dim done As Boolean = False
            Dim isOk As Boolean = True
            Dim loc As New PointF(start.X, start.Y)

            Do Until done
                Dim smvec As New SMVector '(endP.X, endP.Y)

                If XYDistance(loc.X, endP.X, loc.Y, endP.Y) > 5 Then

                    loc = smvec.GeneratePtOnVector(loc, New PointF(endP.X, endP.Y), 3)
                    '// check that location
                    isOk = MovementAllowed(New Point(loc.X, loc.Y), moveType)
                    If isOk = False Then
                        done = True
                        Exit Do
                    End If
                Else
                    Exit Do '// we're close enough
                End If

                'If smvec.Distance(loc.X, loc.Y) > 5 Then
                'Else
                '    '// we can exit with whatever we have
                '    Exit Do
                'End If
            Loop

            Return isOk

        Catch ex As Exception
            HandleException("FlexMapData.MovementFromAToBAllowed", ex)
        End Try
    End Function

    Public Function MovementAllowed(ByVal location As Point, ByVal MoveType As Int16) As Boolean
        Try
            Dim feat As Feature
            Dim i As Int32
            Dim PathIndex As Int32
            Dim lastCovering As Boolean = False
            Dim reg As Region
            ' set the bridgetype at the location before anything else
            Dim btype As BridgeType = LocationBridged(location)

            For i = Features.Count - 1 To 0 Step -1
                ' go backwards dude.
                feat = Features.Item(i)
                If Not feat.isCoveringTerrain Then
                    For PathIndex = 0 To feat.graphPaths.Count - 1
                        ' was dim reg as new region
                        reg = New Region
                        Try
                            ' add each graphics path to this feature's region so we can see if the coords bounce it.
                            reg.Intersect(feat.graphPaths(PathIndex))
                            If reg.IsVisible(location.X, location.Y) Then
                                ' this is the target feature
                                Dim mClass As Integer
                                mClass = feat.moveClass
                                If MovementValues(mClass, MoveType) <> 11 Then
                                    Return True
                                Else
                                    '// check bridges. If a bridge exists, we're okay to continue.
                                    If btype = BridgeType.noBridge OrElse btype = BridgeType.Destroyed Then
                                        Return False
                                    Else
                                        Return True
                                    End If
                                End If
                            End If
                        Catch ex As Exception
                            ' we don't have to do anything. Really.
                            MessageBox.Show(ex.Message & " encountered in MovementAllowed.")
                        End Try
                    Next PathIndex
                End If
            Next i
        Catch ex As Exception
            HandleException("FlexMapData.MovementAllowed", ex)
        End Try
    End Function

    Public Function MovementAllowed(ByVal location As Point, ByVal moveType As Integer, ByVal featses As FeaturesWithRegions) As Long
        Dim featReg As FeatureRegions
        Dim btype As BridgeType = LocationBridged(location)
        Dim value As Integer

        For Each featReg In featses
            If Not featReg.ParentFeature.isCoveringTerrain Then
                Dim reg As Region
                For Each reg In featReg
                    If reg.IsVisible(location) Then
                        Dim mClass As Integer
                        mClass = featReg.ParentFeature.moveClass

                        For moveType = 0 To 10
                            If MovementValues(mClass, moveType) <> 11 Then
                                If moveType = 0 Then
                                    value += 1
                                Else
                                    value += Pow(2, moveType)
                                End If
                            End If
                        Next

                        Return value

                    End If
                Next
            End If
        Next
    End Function

    Public Function AllMovementsAllowed(ByVal location As Point) As Integer
        '// return a value up to 2047 representing which points are impassable for certain movetypes
        Try
            Dim feat As Feature
            Dim i As Int32
            Dim PathIndex As Int32
            'Dim pth As GraphicsPath
            Dim lastCovering As Boolean = False
            'Dim featureName As String
            Dim reg As Region
            Dim moveType As Integer

            Dim AllowableIndex As Integer

            For i = Features.Count - 1 To 0 Step -1
                ' go backwards dude.
                feat = Features.Item(i)
                If Not feat.isCoveringTerrain Then
                    For PathIndex = 0 To feat.graphPaths.Count - 1
                        ' was dim reg as new region
                        reg = New Region

                        ' add each graphics path to this feature's region so we can see if the coords bounce it.
                        reg.Intersect(feat.graphPaths(PathIndex))

                        If reg.IsVisible(location.X, location.Y) Then
                            ' this is the target feature
                            Dim mClass As Integer
                            mClass = feat.moveClass


                            For moveType = 0 To 10
                                If MovementValues(mClass, moveType) <> 11 Then
                                    If moveType = 0 Then
                                        AllowableIndex += 1
                                    Else
                                        AllowableIndex += Pow(2, moveType)
                                    End If
                                End If
                            Next

                        End If

                    Next PathIndex

                End If
            Next i

            Return AllowableIndex

        Catch ex As Exception
            HandleException("FlexMapData.MovementAllowed", ex)
        End Try



    End Function

    Public Function LocationBridged(ByVal pnt As Point, Optional ByVal range As Integer = 5) As BridgeType
        '// returns the type of bridge at the point, within range

        Dim b As Bridge
        For Each b In Me.Bridges
            If XYDistance(b.x, pnt.X, b.y, pnt.Y) <= range Then
                Return b.type
            End If
        Next

        Return BridgeType.noBridge

    End Function

    Public ReadOnly Property MaxMapDimensions() As Short
        Get
            Return MAXMAPSIZE
        End Get
    End Property

    Public ReadOnly Property MinMapDimension() As Short
        Get
            Return MINMAPSIZE
        End Get
    End Property

    Public Property MapScale() As FMPScale
        Get
            ' break the values out into a new fmpscale structure and return it
            Dim Value As New FMPScale
            Dim wp As New WordParser(ExtendedProperties)
            ' parse each word into the extendedproperties array
            Dim prop As String
            Dim ar As New ArrayList(2)

            For Each prop In wp
                ' each item goes into the array
                ar.Add(prop)
            Next
            ' break them out of the array into the correct format
            Value.PixelCount = ar.Item(0)
            Value.MilesPerPixelCount = ar.Item(1)
            Return Value
        End Get
        Set(ByVal Value As FMPScale)
            ' save the values in the extendedproperties property
            ExtendedProperties = Value.PixelCount & " " & Value.MilesPerPixelCount
        End Set
    End Property

    Public Sub ResizeBaseFeature()
        ' this should be called if there is a change in the height or width properties
        If Editable = False Then Exit Sub

        If Not Features Is Nothing Then
            If Features.Count > 0 Then
                Dim pth As GraphicsPath
                Dim feat As Feature = Features.Item(0)
                If feat.graphPaths.Count > 0 Then
                    pth = feat.graphPaths.Item(0)
                    pth.Reset()
                    pth.AddLine(0, 0, Me.Width, 0)
                    pth.AddLine(Me.Width, 0, Me.Width, Me.Height)
                    pth.AddLine(Me.Width, Me.Height, 0, Me.Height)
                    pth.CloseAllFigures()
                End If
            End If
        End If
    End Sub

    Public Function clone() As FlexMapData
        Dim m As New MemoryStream
        Dim b As New BinaryFormatter

        ' before serializing, convert graphpaths to the serializable pointarray and stuff those into feature.specialpaths
        Dim feat As Feature

        For Each feat In Features
            feat.fillSpecialPaths()
        Next

        b.Serialize(m, Me)
        m.Position = 0
        Return b.Deserialize(m)

    End Function

    Public Sub SaveMap(ByVal filePath As String)
        Dim sMap As New FlexMapData
        Try
            ' destroy if it already exists.
            Kill(filePath)
        Catch

        End Try

        Dim fs As New FileStream(filePath, FileMode.Create)
        Dim bin As New BinaryFormatter

        'clear the special paths?
        Dim feat As Feature
        For Each feat In Features
            feat.specialPaths.Clear()
        Next

        ' roads
        '   Clear extended properties
        Dim pixCount As Integer = MapScale.PixelCount
        Dim miles As Integer = MapScale.MilesPerPixelCount

        ExtendedProperties = ""

        Dim aroad As Road
        Dim sbuilder As New System.Text.StringBuilder
        sbuilder.Append(pixCount & " " & miles)
        For Each aroad In Roads
            sbuilder.Append(" " & aroad.Code & " " & aroad.X1 & " " & aroad.Y1 & " " & aroad.X2 & " " & aroad.Y2)
        Next

        ExtendedProperties &= sbuilder.ToString

        sMap = Me.clone
        bin.Serialize(fs, sMap)

    End Sub

    Public Function OpenMap(ByVal filePath As String, Optional ByVal Editable As Boolean = True) As FlexMapData
        Dim fs As New FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)
        Dim bin As New BinaryFormatter
        Dim sMap As New FlexMapData
        Dim legacyMap As Object

        ' the flag editable marks whether the map features can be edited or not.

        Try
            sMap = CType(bin.Deserialize(fs), FlexMapData)
            If sMap.ExtendedProperties = "" Then sMap.ExtendedProperties = "50 1"

        Catch serEx As System.Runtime.Serialization.SerializationException
            fs.Close()
            Dim legBin As New BinaryFormatter
            Dim legfs As New FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)
            legacyMap = legBin.Deserialize(legfs)
        Catch ex As Exception
            Throw ex
        End Try

        If sMap.unitName = "" Then sMap.unitName = "Mi."

        ' convert specialpaths to graphpaths for each feature
        Dim feat As Feature
        Dim erroredFeature As Boolean = False

        For Each feat In sMap.Features
            '// if the feature has no texture, we'll create a generic one.
            If feat.texture Is Nothing Then
                feat.texture = New Bitmap(16, 16)
                erroredFeature = True
            End If

            feat.fillGraphPaths()
        Next

        If erroredFeature Then
            MessageBox.Show("There was a feature missing a texture. An empty texture was created for features with missing textures.", "Missing Texture", MessageBoxButtons.OK)
        End If

        ' ah... do the roads from the extended features... perhaps. Yeah!
        RoadsFromExtendedProperties(sMap)

        ' even if its not, on loading, we want to draw the border ... I think
        baseFeatureDefined = True

        sMap.TemporaryLabels = New MapLabels_fmp
        sMap.Editable = Editable

        Return sMap

        For Each feat In sMap.Features
            feat.specialPaths.Clear()
        Next

    End Function

    Private Sub PopulateFeatureRegions()
        Dim i As Integer
        Dim feat As Feature
        Dim pathindex As Integer

        If FeatureRegs Is Nothing Then FeatureRegs = New FeaturesWithRegions

        '// build up our feature regions (from top to bottom)
        For i = Features.Count - 1 To 0 Step -1
            ' go backwards dude.
            feat = Features.Item(i)

            Dim featregs As New FeatureRegions
            featregs.ParentFeature = feat

            '// now create a region for each path in there and add it to the collection
            For pathindex = 0 To feat.graphPaths.Count - 1
                ' was dim reg as new region
                Dim reg As New Region

                ' add each graphics path to this feature's region so we can see if the coords bounce it.
                reg.Intersect(feat.graphPaths(pathindex))
                featregs.Add(reg)

            Next pathindex

            '// add this featureregions to the collection
            FeatureRegs.Add(featregs)

        Next i
    End Sub

    Private Sub RoadsFromExtendedProperties(ByRef map As FlexMapData)
        ' converts the data in extended properties to roads
        Dim wp As New WordParser(map.ExtendedProperties)
        Dim ar As New Collection
        Dim i As Integer

        Dim s As String
        For Each s In wp
            ar.Add(s)
        Next
        ' remove the first two elements
        ar.Remove(1)
        ar.Remove(1)

        map.Roads = New Roads

        ' roads have an additional "u", "i" or "m" unimproved, improved or major road.

        If ar.Count > 0 Then
            For i = 1 To ar.Count Step 5
                Dim aroad As New Road
                Select Case ar.Item(i)
                    Case "u" : aroad.RoadType = RoadType.Unimproved
                    Case "i" : aroad.RoadType = RoadType.ImprovedRoad
                    Case "m" : aroad.RoadType = RoadType.MajorRoad
                End Select
                aroad.X1 = ar.Item(i + 1)
                aroad.Y1 = ar.Item(i + 2)
                aroad.X2 = ar.Item(i + 3)
                aroad.Y2 = ar.Item(i + 4)
                map.Roads.Add(aroad)
            Next
        End If

    End Sub

    Public Sub BuildBaseTerrain()
        ' create the first feature (base terrain) the size of the map
        Dim grPath As New GraphicsPath
        grPath.AddLine(0, 0, Me.Width, 0)
        grPath.AddLine(Me.Width, 0, Me.Width, Me.Height)
        grPath.AddLine(0, Me.Height, 0, 0)
        grPath.CloseFigure()

        Dim feat As Feature = Features(0)
        feat.graphPaths.Add(grPath)
        DefineDefaults()
        baseFeatureDefined = True

    End Sub

    Public Sub Render(ByVal grender As Graphics, ByVal clipRect As RectangleF, Optional ByVal lineWork As Boolean = True, Optional ByVal showBoundaries As Boolean = False, Optional ByVal scaleFactorX As Single = -1, Optional ByVal scaleFactorY As Single = -1, Optional ByVal offSetX As Int16 = 0, Optional ByVal offSetY As Int16 = 0, Optional ByVal BaseFeatureBorder As Boolean = True, Optional ByVal HardRender As Boolean = True, Optional ByVal Topographic As Boolean = False)
        'loop through each feature and add paint it. 
        ' a hard render renders all features, and basically redraws all features. This is used for editing.
        ' during a game, hardrender should be FALSE after opening the file so it just paints.
        Try
            Dim br As Brush
            Dim feat As Feature
            Dim gPath As GraphicsPath

            RaiseEvent RenderRequest()

            ' clipping plane
            Dim reg As New Region(New Rectangle(0, 0, Me.Width, Me.Height))
            grender.SetClip(reg, CombineMode.Intersect)

            grender.Clear(Color.Black)

            If (scaleFactorX <> -1) Or (scaleFactorY <> -1) Then
                grender.ScaleTransform(scaleFactorX, scaleFactorY)
            End If


            If HardRender = True Then
                If BaseBMP Is Nothing Then BaseBMP = New Bitmap(Me.Width, Me.Height)
                Dim baseGraphics As Graphics = Graphics.FromImage(BaseBMP)

                For Each feat In Me.Features
                    Dim i As Int16

                    If Topographic = False Then

                        If TypeOf feat Is Elevation Then
                            Dim c As Integer = DirectCast(feat, Elevation).ElevationRank * 20
                            br = New SolidBrush(Color.FromArgb(0, 0, 0, 0))
                        Else
                            feat.texture.MakeTransparent(Color.FromArgb(255, 0, 255))
                            br = New TextureBrush(feat.texture)
                        End If

                    Else
                        '// this means SHOW elevation.
                        'If TypeOf feat Is ElevationFeature Then
                        '    Dim efeat As ElevationFeature = DirectCast(feat, ElevationFeature)
                        '    br = New SolidBrush(Color.FromArgb(efeat.Elevation, efeat.Elevation, efeat.Elevation))
                        'End If

                        If TypeOf feat Is Elevation Then
                            Dim c As Integer = DirectCast(feat, Elevation).ElevationRank * 20
                            br = New SolidBrush(Color.FromArgb(60, c, c, c))
                        Else
                            feat.texture.MakeTransparent(Color.FromArgb(255, 0, 255))
                            br = New TextureBrush(feat.texture)
                        End If

                    End If

                    If Not feat.graphPaths Is Nothing Then
                        If feat.graphPaths.Count > 0 Then
                            For Each gPath In feat.graphPaths
                                gPath = feat.graphPaths(i)
                                grender.FillPath(br, gPath)
                                ' render also into the base bmp
                                baseGraphics.FillPath(br, gPath)
                                If TypeOf feat Is Elevation Then
                                    Dim p As New Pen(Color.Blue, 6 - DirectCast(feat, Elevation).ElevationRank)
                                    grender.DrawPath(p, gPath)
                                    p.Dispose()
                                Else
                                    If showBoundaries = True Then
                                        grender.DrawPath(Pens.BlanchedAlmond, gPath)
                                        ' render the base bmp too
                                        baseGraphics.DrawPath(Pens.BlanchedAlmond, gPath)
                                    End If
                                End If
                                i = i + 1
                            Next
                        End If
                    End If
                    i = 0
                Next

                If BaseFeatureBorder = True Then
                    If baseFeatureDefined = True Then
                        Dim rect As New Rectangle(0, 0, Me.Width, Me.Height)
                        Dim p As New Pen(Color.Red)

                        p.Width = 3
                        grender.DrawRectangle(p, rect)
                        ' render base bmp
                        baseGraphics.DrawRectangle(p, rect)
                    End If
                End If
            Else ' this is an after-load rendering and can simply use the internally set bitmap.
                ' draw the basebmp onto the graphics object
                grender.DrawImage(BaseBMP, New Point(0, 0))
            End If

            Try
                br.Dispose()     ' sometimes this brush is ignored
            Catch

            End Try

            RaiseEvent RenderComplete()
        Catch ex As Exception
            HandleException("FlexMapData.Render", ex)
        End Try


    End Sub

    Public Function MovementCost(ByVal pnt1 As Point, ByVal moveType As Long) As Single
        ' returns the movement cost at a given point for a provided movement type... a percentage cost total
        Dim cost As Single
        Dim moveClass As Long
        Dim secondaryCost As Single
        Dim fullMove As Single

        fullMove = 100

        If moveType > MAXMOVEMENTTYPES Then
            Return 0
        End If

        ' find the feature at the coordinates and find its base movement value and its base cost.
        BaseCost(pnt1, cost)

        ' edit the full move by the point cost
        fullMove -= (fullMove * (cost * 0.01))

        ' get the movement class of the predominant terrain feature.
        moveClass = MoveClassAtCoord(pnt1)

        secondaryCost = MovementValues(moveClass, moveType)
        fullMove -= (fullMove * (secondaryCost * 0.01))

        ' return the total.
        Return fullMove

    End Function

    Private Sub BaseCost(ByVal pnt As Point, ByRef cst As Single)
        ' assigns the base terrain cost, keeping in mind covering terrain.
        Dim feat As Feature
        Dim i As Int32
        Dim PathIndex As Int32
        Dim lastCovering As Boolean = False

        For i = Features.Count - 1 To 0 Step -1
            ' go backwards dude.
            feat = Features.Item(i)
            For PathIndex = 0 To feat.graphPaths.Count
                Dim reg As New Region
                Try
                    ' add each graphics path to this feature's region so we can see if the coords bounce it.
                    reg.Intersect(feat.graphPaths(PathIndex))
                    If reg.IsVisible(pnt.X, pnt.Y) Then
                        If feat.isCoveringTerrain Then
                            ' we should continue looking at features... 
                            If lastCovering = True Then
                                ' we don't do anything with multiple covering terrains... 
                            Else
                                cst += feat.moveValue
                                lastCovering = True
                            End If
                        Else
                            ' we found the topmost non-covering terrain, so we are done.
                            cst += feat.moveValue
                            Exit Sub
                        End If
                    End If
                Catch
                    ' we don't have to do anything. Really.
                End Try
            Next PathIndex
        Next i

    End Sub

    Public Sub MoveClassLOSAndDefenseAtCoord(ByVal pnt As Point, ByRef moveType As Int16, ByVal MoveModifier As Single, ByRef def As Int16, ByRef los As Single)
        '// this sub does NOTHING with covering terrain
        Dim feat As Feature
        Dim i As Int32
        Dim PathIndex As Int32
        'Dim pth As GraphicsPath
        Dim lastCovering As Boolean = False
        Dim coveringCost As Int16

        For i = Features.Count - 1 To 0 Step -1
            ' go backwards dude.
            feat = Features.Item(i)
            For PathIndex = 0 To feat.graphPaths.Count
                Dim reg As New Region
                Try
                    ' add each graphics path to this feature's region so we can see if the coords bounce it.
                    reg.Intersect(feat.graphPaths(PathIndex))
                    If reg.IsVisible(pnt.X, pnt.Y) Then
                        If feat.isCoveringTerrain Then
                            ' we should continue looking at features... 
                            If lastCovering = True Then
                                ' we don't do anything with multiple covering terrains... 
                            Else
                                coveringCost = feat.LOS
                                lastCovering = True
                            End If
                        Else
                            ' we found the topmost non-covering terrain, so we are done.
                            MoveModifier = MovementValues(feat.moveClass, moveType) / 100
                            def = feat.defensive
                            los = feat.LOS
                            If lastCovering Then    ' covering terrain was found
                                los += coveringCost
                            End If
                            Exit Sub
                        End If
                    End If
                Catch
                    ' we don't have to do anything. Really.
                End Try
            Next PathIndex
        Next i
    End Sub

    Public Function MoveClassAtCoord(ByVal pnt As Point) As Long
        Dim lastCovering As Boolean = False
        Dim featR As FeatureRegions
        Dim reggie As Region

        If Not bFeatureregionsInitialized Then
            PopulateFeatureRegions()
            bFeatureregionsInitialized = True
        End If

        Try
            For Each featR In FeatureRegs
                For Each reggie In featR
                    If reggie.IsVisible(pnt) Then
                        Return featR.ParentFeature.moveClass
                    End If
                Next
            Next
        Catch ex As Exception
            HandleException("FlexMapData.MoveClassAtCoord", ex)
        End Try

        ''''For i = Features.Count - 1 To 0 Step -1
        ''''    ' go backwards dude.
        ''''    feat = Features.Item(i)
        ''''    For PathIndex = 0 To feat.graphPaths.Count

        ''''        Try
        ''''            ' add each graphics path to this feature's region so we can see if the coords bounce it.
        ''''            reg.Intersect(feat.graphPaths(PathIndex))
        ''''            If reg.IsVisible(pnt.X, pnt.Y) Then
        ''''                If feat.isCoveringTerrain Then
        ''''                    ' we don't do anything special for covering terrain.
        ''''                Else
        ''''                    ' we found the topmost non-covering terrain, so we are done.
        ''''                    Return feat.moveClass
        ''''                    Exit Function
        ''''                End If
        ''''            End If
        ''''        Catch
        ''''            ' we don't have to do anything. Really.
        ''''        End Try
        ''''    Next PathIndex
        ''''Next i
        ' if all else fails:
        Return 0

    End Function

    Public Function DefenseAtCoord(ByVal pnt As Point) As Long
        Dim feat As Feature
        Dim i As Int32
        Dim PathIndex As Int32
        'Dim pth As GraphicsPath
        Dim lastCovering As Boolean = False

        For i = Features.Count - 1 To 0 Step -1
            ' go backwards dude.
            feat = Features.Item(i)
            For PathIndex = 0 To feat.graphPaths.Count
                Dim reg As New Region
                Try
                    ' add each graphics path to this feature's region so we can see if the coords bounce it.
                    reg.Intersect(feat.graphPaths(PathIndex))
                    If reg.IsVisible(pnt.X, pnt.Y) Then
                        If feat.isCoveringTerrain Then
                            ' we don't do anything special for covering terrain.
                        Else
                            ' we found the topmost non-covering terrain, so we are done.
                            Return feat.defensive
                            Exit Function
                        End If
                    End If
                Catch
                    ' we don't have to do anything. Really.
                End Try
            Next PathIndex
        Next i
        ' if all else fails:
        Return 0
    End Function

    Public Function DefenseAtCoord(ByVal pntf As PointF) As Long
        Return DefenseAtCoord(New Point(pntf.X, pntf.Y))
    End Function

    Public Function LOSCost(ByVal pnt1 As Point) As Single
        ' Returns the cost of the terrain type based on a percent modifier.
        Dim normalValue As Single = 1  ' 1.0 means NO modification

        'Dim i As Int32
        Dim lastCovering As Boolean = False
        Dim coveringCost As Single
        Dim terrainCost As Single
        Dim featRegs As FeatureRegions
        Dim reggie As Region
        Dim done As Boolean

        '// if the point is outside the size of the map, return "no fricken way".
        If pnt1.X <= 0 OrElse pnt1.Y <= 0 OrElse pnt1.X > Me.Width OrElse pnt1.Y > Me.Height Then
            Return 10000
        End If

        If FeatureRegs Is Nothing Then PopulateFeatureRegions()

        For Each featRegs In Me.FeatureRegs
            If done Then Exit For

            For Each reggie In featRegs
                If reggie.IsVisible(pnt1) Then
                    If featRegs.ParentFeature.isCoveringTerrain Then
                        If lastCovering = True Then
                            '/// blah
                        Else
                            coveringCost = (featRegs.ParentFeature.LOS)
                            lastCovering = True
                        End If
                    Else
                        terrainCost = (featRegs.ParentFeature.LOS)
                        done = True
                        Exit For
                    End If
                End If
            Next
        Next

        normalValue *= (terrainCost + coveringCost)

        Return (normalValue / 100)

    End Function

    Public Function LOSCost(ByVal pnt1 As Point, ByVal pnt2 As Point) As Long
        ' put in the cost of LOS at the given point, plus the distance and the cost at the target point
        Return 0
    End Function

#Region "Define Defaults"
    Public Sub DefineDefaults()
        ' ground chit default movement classes
        MoveClassLookup(0) = "Open"
        MoveClassLookup(1) = "Elevation"
        MoveClassLookup(2) = "Rough"
        MoveClassLookup(3) = "Difficult Elevation"
        MoveClassLookup(4) = "Dense"
        MoveClassLookup(5) = "Difficult"
        MoveClassLookup(6) = "Dangerous"
        MoveClassLookup(7) = "Shallow Water"
        MoveClassLookup(8) = "Water"
        MoveClassLookup(9) = "Deep Water"
        ' ground chit default movement types
        MoveTypeLookup(0) = "Infantry"
        MoveTypeLookup(1) = "Mechanized"
        MoveTypeLookup(2) = "Fast"
        MoveTypeLookup(3) = "Mounted"
        MoveTypeLookup(4) = "Skimmer"
        MoveTypeLookup(5) = "Wheeled"
        MoveTypeLookup(6) = "Tracked"
        MoveTypeLookup(7) = "Heavy"
        MoveTypeLookup(8) = "Mixed"
        MoveTypeLookup(9) = "Riverine"
        MoveTypeLookup(10) = "Sea"

        ' basic default movement value matrix
        ' open
        MovementValues(0, 0) = 0 ' infantry
        MovementValues(0, 1) = 0 ' mechanized
        MovementValues(0, 2) = 0 ' fast
        MovementValues(0, 3) = 0
        MovementValues(0, 4) = 0
        MovementValues(0, 5) = 0
        MovementValues(0, 6) = 0
        MovementValues(0, 7) = 0
        MovementValues(0, 8) = 0
        MovementValues(0, 9) = IMPASSABLEVALUE
        MovementValues(0, 10) = IMPASSABLEVALUE
        'elevation
        MovementValues(1, 0) = 30 ' infantry
        MovementValues(1, 1) = 40 ' mechanized
        MovementValues(1, 2) = 0 ' fast
        MovementValues(1, 3) = 40
        MovementValues(1, 4) = 20
        MovementValues(1, 5) = 50
        MovementValues(1, 6) = 40
        MovementValues(1, 7) = 60
        MovementValues(1, 8) = 10
        MovementValues(1, 9) = IMPASSABLEVALUE
        MovementValues(1, 10) = IMPASSABLEVALUE
        ' rough
        MovementValues(2, 0) = 20
        MovementValues(2, 1) = 30
        MovementValues(2, 2) = 30
        MovementValues(2, 3) = 30
        MovementValues(2, 4) = 0
        MovementValues(2, 5) = 40
        MovementValues(2, 6) = 10
        MovementValues(2, 7) = 10
        MovementValues(2, 8) = 40
        MovementValues(2, 9) = IMPASSABLEVALUE
        MovementValues(2, 10) = IMPASSABLEVALUE
        'difficult elevation
        MovementValues(3, 0) = 50
        MovementValues(3, 1) = 60
        MovementValues(3, 2) = 80
        MovementValues(3, 3) = 60
        MovementValues(3, 4) = 40
        MovementValues(3, 5) = 70
        MovementValues(3, 6) = 60
        MovementValues(3, 7) = 80
        MovementValues(3, 8) = 80
        MovementValues(3, 9) = IMPASSABLEVALUE
        MovementValues(3, 10) = IMPASSABLEVALUE
        ' dense
        MovementValues(4, 0) = 20
        MovementValues(4, 1) = 50
        MovementValues(4, 2) = 50
        MovementValues(4, 3) = 40
        MovementValues(4, 4) = 60
        MovementValues(4, 5) = 50
        MovementValues(4, 6) = 50
        MovementValues(4, 7) = 80
        MovementValues(4, 8) = 80
        MovementValues(4, 9) = IMPASSABLEVALUE
        MovementValues(4, 10) = IMPASSABLEVALUE
        'difficult
        MovementValues(5, 0) = 40
        MovementValues(5, 1) = 50
        MovementValues(5, 2) = 60
        MovementValues(5, 3) = 50
        MovementValues(5, 4) = 30
        MovementValues(5, 5) = 50
        MovementValues(5, 6) = 30
        MovementValues(5, 7) = 30
        MovementValues(5, 8) = 60
        MovementValues(5, 9) = IMPASSABLEVALUE
        MovementValues(5, 10) = IMPASSABLEVALUE
        'dangerous
        MovementValues(6, 0) = 80
        MovementValues(6, 1) = 80
        MovementValues(6, 2) = 80
        MovementValues(6, 3) = 80
        MovementValues(6, 4) = 80
        MovementValues(6, 5) = 80
        MovementValues(6, 6) = 80
        MovementValues(6, 7) = 80
        MovementValues(6, 8) = 80
        MovementValues(6, 9) = IMPASSABLEVALUE
        MovementValues(6, 10) = IMPASSABLEVALUE
        'shallowwater
        MovementValues(7, 0) = 60
        MovementValues(7, 1) = 40
        MovementValues(7, 2) = 40
        MovementValues(7, 3) = 50
        MovementValues(7, 4) = 0
        MovementValues(7, 5) = 50
        MovementValues(7, 6) = 40
        MovementValues(7, 7) = 20
        MovementValues(7, 8) = 60
        MovementValues(7, 9) = 0
        MovementValues(7, 10) = IMPASSABLEVALUE
        'water
        MovementValues(8, 0) = IMPASSABLEVALUE
        MovementValues(8, 1) = IMPASSABLEVALUE
        MovementValues(8, 2) = IMPASSABLEVALUE
        MovementValues(8, 3) = IMPASSABLEVALUE
        MovementValues(8, 4) = 0
        MovementValues(8, 5) = IMPASSABLEVALUE
        MovementValues(8, 6) = IMPASSABLEVALUE
        MovementValues(8, 7) = 50
        MovementValues(8, 8) = IMPASSABLEVALUE
        MovementValues(8, 9) = 50
        MovementValues(8, 10) = 40
        'deep water
        MovementValues(9, 0) = IMPASSABLEVALUE
        MovementValues(9, 1) = IMPASSABLEVALUE
        MovementValues(9, 2) = IMPASSABLEVALUE
        MovementValues(9, 3) = IMPASSABLEVALUE
        MovementValues(9, 4) = IMPASSABLEVALUE
        MovementValues(9, 5) = IMPASSABLEVALUE
        MovementValues(9, 6) = IMPASSABLEVALUE
        MovementValues(9, 7) = IMPASSABLEVALUE
        MovementValues(9, 8) = IMPASSABLEVALUE
        MovementValues(9, 9) = IMPASSABLEVALUE
        MovementValues(9, 10) = 0


        'Dim i As Integer
        'Dim b As Integer
        'For i = 0 To 9
        '    For b = 0 To 10
        '        MovementValues(i, b) = 10 + b + i 'CStr(i.ToString & b.ToString)
        '    Next
        'Next


        ' set up map type 'C' bridge movement allowances
        ' BridgeMoveAllowances(x,y)
        ' x is bridge type: 0 temp, 1 small, 2 bridge, 3 fortified/heavy
        ' y is move type: 0 inf, 1 mech, 2 fast, 3 mounted, 4 skimmer, 5, wheeled, 6 tracked, 7 heavy walker
        '        8 mixed, 9 riverine, 10 sea
        ReDim BridgeMoveAllowances(3, 10)
        BridgeMoveAllowances(0, 0) = 1
        BridgeMoveAllowances(0, 1) = 0
        BridgeMoveAllowances(0, 2) = 0
        BridgeMoveAllowances(0, 3) = 1
        BridgeMoveAllowances(0, 4) = 1
        BridgeMoveAllowances(0, 5) = 1
        BridgeMoveAllowances(0, 6) = 0
        BridgeMoveAllowances(0, 7) = 0
        BridgeMoveAllowances(0, 8) = 0
        BridgeMoveAllowances(0, 9) = 0
        BridgeMoveAllowances(0, 10) = 0

        BridgeMoveAllowances(1, 0) = 1
        BridgeMoveAllowances(1, 1) = 1
        BridgeMoveAllowances(1, 2) = 1
        BridgeMoveAllowances(1, 3) = 1
        BridgeMoveAllowances(1, 4) = 1
        BridgeMoveAllowances(1, 5) = 1
        BridgeMoveAllowances(1, 6) = 1
        BridgeMoveAllowances(1, 7) = 0
        BridgeMoveAllowances(1, 8) = 1
        BridgeMoveAllowances(1, 9) = 0
        BridgeMoveAllowances(1, 10) = 0

        BridgeMoveAllowances(2, 0) = 1
        BridgeMoveAllowances(2, 1) = 1
        BridgeMoveAllowances(2, 2) = 1
        BridgeMoveAllowances(2, 3) = 1
        BridgeMoveAllowances(2, 4) = 1
        BridgeMoveAllowances(2, 5) = 1
        BridgeMoveAllowances(2, 6) = 1
        BridgeMoveAllowances(2, 7) = 0
        BridgeMoveAllowances(2, 8) = 1
        BridgeMoveAllowances(2, 9) = 1
        BridgeMoveAllowances(2, 10) = 0

        BridgeMoveAllowances(3, 0) = 1
        BridgeMoveAllowances(3, 1) = 1
        BridgeMoveAllowances(3, 2) = 1
        BridgeMoveAllowances(3, 3) = 1
        BridgeMoveAllowances(3, 4) = 1
        BridgeMoveAllowances(3, 5) = 1
        BridgeMoveAllowances(3, 6) = 1
        BridgeMoveAllowances(3, 7) = 1
        BridgeMoveAllowances(3, 8) = 1
        BridgeMoveAllowances(3, 9) = 1
        BridgeMoveAllowances(3, 10) = 1

    End Sub
#End Region

    Public Sub ZOrderFeatures()
        Dim frmZ As New ZOrderFeature
        frmZ.ShowForm(Features)
        frmZ.Dispose()
    End Sub

    <Serializable()> Public Class Bridge
        Public type As FlexMapData.BridgeType
        Public x As Int32
        Public y As Int32
        Public name As String
        Public integrity As Int16

        Overrides Function ToString() As String
            Return name
        End Function

    End Class

    Public Function XYDistance(ByVal X1 As Single, ByVal X2 As Single, ByVal Y1 As Single, ByVal Y2 As Single) As Integer
        'returns the distance between two 2d coordinates
        XYDistance = Int(Abs(Sqrt(((X1 - X2) * (X1 - X2)) + ((Y1 - Y2) * (Y1 - Y2)))))

    End Function

End Class


<Serializable()> Public Class Type2FlexMapData
    '// extended version of the map. This one allows the placement of structures for flexmap world builder.
    '// this is used specifically for creating a battlemap.
    Inherits FlexMapData

    Private m_Structures As New Type2Structures

    Public Enum Type2Structures_t2s
        ImperialBlockHouse = 1
        ImperialAdministorum = 2

        OrkFlatHouse = 24

        Ruin = 48
    End Enum

    Public Property Structures() As Type2Structures
        Get
            Return m_Structures
        End Get
        Set(ByVal Value As Type2Structures)
            m_Structures = Value
        End Set
    End Property

End Class


Public Class MapLabels_fmp
    Inherits CollectionBase

    Default Public Property Item(ByVal index As Integer)
        Get
            Return innerlist(index)
        End Get
        Set(ByVal Value)
            innerlist.Item(index) = Value
        End Set
    End Property

    Public Sub Remove(ByVal ml As FlexMapData.MapLabel)
        innerlist.Remove(ml)
    End Sub

    Public Sub Add(ByVal ml As FlexMapData.MapLabel)
        innerlist.Add(ml)
    End Sub

End Class


Public Class WordParser
    ' this is a useful Text Command Interpreter. Nice!
    Implements IEnumerable

    ' the parse sentence
    Private m_source As String

    Sub New(ByVal sourceString As String)
        m_source = sourceString
    End Sub

    Function getEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        Return New WordParserEnumerator(m_source)
    End Function

    Public Property Source() As String
        Get
            Return m_source
        End Get
        Set(ByVal Value As String)
            m_source = Value
        End Set
    End Property

    Class WordParserEnumerator
        Implements IEnumerator

        Dim source As String

        Dim CharIndex As Integer
        Dim CurrentWord As String
        Dim re As New System.Text.RegularExpressions.Regex("\w+")

        Sub New(ByVal sourceString As String)
            source = sourceString
            Reset()
        End Sub

        Private Sub Reset() Implements IEnumerator.Reset
            CharIndex = 0
        End Sub

        Private ReadOnly Property Current() As Object Implements IEnumerator.Current
            Get
                Return CurrentWord
            End Get
        End Property

        Private Function MoveNext() As Boolean Implements IEnumerator.MoveNext
            '// find the next word
            Dim mat As System.Text.RegularExpressions.Match
            mat = re.Match(source, CharIndex)
            '// return false if not found
            If Not mat.Success Then Return False

            '// hey, not false, found word... all is good
            CurrentWord = mat.Value
            '// remember position
            CharIndex = mat.Index + mat.Length '// end of the current value muchacho
            Return True

        End Function

    End Class

End Class


Public Class FeaturesWithRegions
    Inherits CollectionBase

    Default Public Property Item(ByVal index As Integer) As FeatureRegions
        Get
            Return innerlist.Item(index)
        End Get
        Set(ByVal Value As FeatureRegions)
            innerlist.Item(index) = Value
        End Set
    End Property

    Public Sub Add(ByRef item As FeatureRegions)
        innerlist.Add(item)
    End Sub

    Public Sub remove(ByRef item As FeatureRegions)
        innerlist.Remove(item)
    End Sub

End Class


Public Class FeatureRegions
    Inherits CollectionBase

    Private m_Feature As Feature

    Default Public Property Item(ByVal index As Integer) As Region
        Get
            Return innerlist.Item(index)
        End Get
        Set(ByVal Value As Region)
            innerlist.Item(index) = Value
        End Set
    End Property

    Public Property ParentFeature() As Feature
        Get
            Return m_Feature
        End Get
        Set(ByVal Value As Feature)
            m_Feature = Value
        End Set
    End Property

    Public Sub Add(ByVal item As Region)
        innerlist.Add(item)
    End Sub

    Public Sub Remove(ByRef item As Region)
        innerlist.Remove(item)
    End Sub

End Class


<Serializable()> Public Class ElevationFeature
    Inherits Feature

    '// adds the elevation attribute

    Private m_Elevation As Integer

    Public Property Elevation() As Integer
        Get
            Return m_Elevation
        End Get
        Set(ByVal Value As Integer)
            m_Elevation = Value
        End Set
    End Property


End Class


<Serializable()> Public Class Type2Structures
    Inherits CollectionBase

    Default Public Property Item(ByVal index As Integer) As Type2Structure
        Get
            Return innerlist.Item(index)
        End Get
        Set(ByVal Value As Type2Structure)
            innerlist.Item(index) = Value
        End Set
    End Property

    Public Sub Add(ByRef item As Type2Structure)
        innerlist.Add(item)
    End Sub

    Public Sub remove(ByRef item As Type2Structure)
        innerlist.Remove(item)
    End Sub
End Class


<Serializable()> Public Class Type2Structure
    Private m_structureType As Type2FlexMapData.Type2Structures_t2s
    Private m_xlocation As Long
    Private m_ylocation As Long

    Public Property StructureType() As Type2FlexMapData.Type2Structures_t2s
        Get
            Return m_structureType
        End Get
        Set(ByVal Value As Type2FlexMapData.Type2Structures_t2s)
            m_structureType = Value
        End Set
    End Property

    Public Property XLocation() As Long
        Get
            Return m_xlocation
        End Get
        Set(ByVal Value As Long)
            m_xlocation = Value
        End Set
    End Property

    Public Property YLocation() As Long
        Get
            Return m_ylocation
        End Get
        Set(ByVal Value As Long)
            m_ylocation = Value
        End Set
    End Property

End Class
